﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description: ReferencedApplicationPayloadViewModel
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Input;
using VmroleAuthor.Commands;
using VmroleAuthor.Helpers;
using VmroleAuthor.Models;
using MessageBox = System.Windows.Forms.MessageBox;

namespace VmroleAuthor.ViewModels
{
    public class ReferencedApplicationPayloadViewModel : ViewModelBase, IDataErrorInfo, ICleanupParamsAndPayload
    {
        private readonly VmRoleResourceExtensionViewModel vmRoleResourceExtensionViewModel;
        private readonly ValidationHelper.PayloadType payloadType;
        private readonly Func<string> appPayloadReferenceGetter;
        private readonly Action<string> appPayloadReferenceSetter;
        private ApplicationPayloadViewModel selectedApplicationPayload;
        private readonly bool isPayloadRequired;
        private bool isExternalChanged;
        private readonly string propertyName;       

        /// <summary>
        /// These must be applied/updated by pressing a button to apply to the app profile payload
        /// </summary>
        private string selectedAppPayloadItemPath;
        private string payloadId;
        private bool isExternal;      

        public static class PropertyNames
        {
            public const string SelectedAppPayloadItemPath = "SelectedAppPayloadItemPath";
            public const string PayloadId = "PayloadId";
            public const string IsExternal = "IsExternal";           
        }  


        public ReferencedApplicationPayloadViewModel(
            VmRoleResourceExtensionViewModel roleResourceExtensionViewModel,
            ValidationHelper.PayloadType payloadType,
            Func<string> appPayloadReferenceGetter,
            Action<string> appPayloadReferenceSetter,
            bool isPayloadMandatory,
            string propertyName)
        {
            this.vmRoleResourceExtensionViewModel = roleResourceExtensionViewModel;
            this.payloadType = payloadType;
            this.appPayloadReferenceGetter = appPayloadReferenceGetter;
            this.appPayloadReferenceSetter = appPayloadReferenceSetter;
            this.isPayloadRequired = isPayloadMandatory;
            this.propertyName = propertyName;

            

            this.payloadId = this.appPayloadReferenceGetter();
            this.selectedApplicationPayload = roleResourceExtensionViewModel.ApplicationProvileViewModel.GetApplicationPayloadViewModel(this.payloadId);
            if (this.selectedApplicationPayload != null)
            {
                this.selectedAppPayloadItemPath = this.selectedApplicationPayload.RelativePath;
                this.isExternal = this.vmRoleResourceExtensionViewModel.ResourceExtensionDataPackageViewModel.ExternalPayloadStartingWithPrefixExists( this.selectedAppPayloadItemPath) 
                    &&  !this.PayloadExists();         
            }

            this.SelectApplicationPayloadCommand = new RelayCommand(
             param => this.SelectApplicationPayload(),
             param => this.CanSelectApplicationPayload());

            this.UpdateApplicationPayloadCommand = new RelayCommand(
                param => this.UpdateApplicationPayload(),
                param => this.CanUpdateApplicationPayload());             
        }


        public string SelectedAppPayloadItemPath
        {
            get { return this.selectedAppPayloadItemPath; }
            set
            {
                this.Cleanup();
                this.selectedAppPayloadItemPath = value;
                this.OnPropertyChanged(PropertyNames.SelectedAppPayloadItemPath);
                this.OnPropertyChanged(propertyName);
            }
        }

        public string PayloadId
        {
            get { return this.payloadId; }
            set
            {
                this.payloadId = value;
                this.OnPropertyChanged(PropertyNames.PayloadId);
                this.OnPropertyChanged(propertyName);
            }
        }

        public bool IsExternal
        {
            get { return this.isExternal; }
            set
            {  
                if (this.IsExternal != value)
                {
                    this.isExternalChanged = true;
                }
                this.isExternal = value;
                this.OnPropertyChanged(PropertyNames.IsExternal);
                this.OnPropertyChanged(propertyName);
            }
        }       

        public ICommand SelectApplicationPayloadCommand { get; private set; }

        public bool CanSelectApplicationPayload()
        {
            return true;
        }

        public void SelectApplicationPayload()
        {
            this.Cleanup();
            string initialPath =string.Empty;
            if (this.vmRoleResourceExtensionViewModel.ResourceExtensionDefinition.DataPackage != null &&
                    !string.IsNullOrEmpty(this.vmRoleResourceExtensionViewModel.ResourceExtensionDefinition.DataPackage.Location) &&
                    this.vmRoleResourceExtensionViewModel.ResourceExtensionStorageInfo != null &&
                    this.vmRoleResourceExtensionViewModel.ResourceExtensionStorageInfo.WorkingDir != null)
                {
                    initialPath = System.IO.Path.Combine(this.vmRoleResourceExtensionViewModel.ResourceExtensionStorageInfo.WorkingDir,this.vmRoleResourceExtensionViewModel.ResourceExtensionDefinition.DataPackage.Location);                   
                }

            if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
            {
                using (FolderBrowserDialog dlg = new FolderBrowserDialog())
                {
                    dlg.Description = "Script Command Folder (.cr)";                    
                    dlg.ShowNewFolderButton = false;
                    if (System.IO.Directory.Exists(initialPath))
                    {
                        dlg.SelectedPath = initialPath;                        
                    }
                    DialogResult result = dlg.ShowDialog();
                    if (result == System.Windows.Forms.DialogResult.OK)
                    {
                        this.selectedAppPayloadItemPath = dlg.SelectedPath;                       
                    }
                }
            }
            else
            {
                StringBuilder filterSb = new StringBuilder();
                switch (this.payloadType)
                {
                    case ValidationHelper.PayloadType.WebDeployPackage:
                        filterSb.Append("Web Deploy Package (*.zip)|*.zip");
                        break;
                    case ValidationHelper.PayloadType.SqlDac:
                        filterSb.Append("SQL Dac Package (*.dacpac)|*.dacpac");                  
                        break;
                    case ValidationHelper.PayloadType.SqlScript:
                        filterSb.Append("SQL Script (*.sql)|*.sql");
                        break;
                    case ValidationHelper.PayloadType.SQLConfigurationFile:
                        filterSb.Append("SQL Configuration File (*.ini)|*.ini");
                        break;
                }
                filterSb.Append("|All Files (*.*)|*.*");

                Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog
                    {
                        DefaultExt = ".*",
                        Filter = filterSb.ToString(),
                        Title = "Open Resource Extension Package or Resource Definition Package or Resource Definition"
                    };

                if (System.IO.Directory.Exists(initialPath))
                {
                    dlg.InitialDirectory = initialPath;
                }             

                bool? result = dlg.ShowDialog();
                // Process open file dialog box results 
                if ((result == true) && (dlg.FileName != null))
                {
                    this.selectedAppPayloadItemPath = dlg.FileName;
                }
            }
            
            this.OnPropertyChanged(PropertyNames.SelectedAppPayloadItemPath);
            this.OnPropertyChanged(PropertyNames.PayloadId);
            this.OnPropertyChanged(propertyName);
        }

        public ICommand UpdateApplicationPayloadCommand { get; private set; }

        public bool CanUpdateApplicationPayload()
        {
            return true;
        }


        public void UpdateApplicationPayload()
        {
            if (this.isExternalChanged && !this.IsExternal)
            {
                // Cleanup External references
                this.CleanupExternalPayload();
            }
            if (string.IsNullOrEmpty(this.selectedAppPayloadItemPath))
            {
                //User is clearing payload
                this.Cleanup();
                this.payloadId = null;
                this.appPayloadReferenceSetter(null);
            }
            else
            {
                if (this.vmRoleResourceExtensionViewModel.ResourceExtensionDefinition.DataPackage == null ||
                string.IsNullOrEmpty(this.vmRoleResourceExtensionViewModel.ResourceExtensionDefinition.DataPackage.Location))
                {
                    // Datapackage Location not set. So set defaults
                    this.vmRoleResourceExtensionViewModel.ResourceExtensionDataPackageViewModel.Location = ResourceExtensionDataPackageViewModel.DefaultDataPackageLocation;
                    if (string.IsNullOrEmpty(this.vmRoleResourceExtensionViewModel.ResourceExtensionDataPackageViewModel.Version))
                    {
                        this.vmRoleResourceExtensionViewModel.ResourceExtensionDataPackageViewModel.Version = ResourceExtensionDataPackageViewModel.DefaultDataPackageVersion;
                    }
                }

                // Basic checks before applyin
                string error = this.ValidateBasicRequirementsForSelectedAppPayloadItemPath();
                if (error != null)
                {
                    MessageBox.Show("Error: " + error, DefinitionViewModel.ToolHeader);
                    this.OnPropertyChanged(PropertyNames.SelectedAppPayloadItemPath);
                    this.OnPropertyChanged(PropertyNames.PayloadId);
                    return;
                }

                //Is Relative
                if (this.IsSelectedAppPayloadItemPathARelativePath())
                {
                    this.ProcessUpdateForSelectedRelativePath();
                }
                else
                {
                    //this in turn copies to datapackage dir and processes update for relative path
                    this.ProcessUpdateForSelectedAbsolutePath();
                }
            }


            
           this.isExternalChanged = false;
           
           this.OnPropertyChanged(PropertyNames.SelectedAppPayloadItemPath);
           this.OnPropertyChanged(PropertyNames.PayloadId);
           this.OnPropertyChanged(PropertyNames.IsExternal);
           this.OnPropertyChanged(propertyName);
        }
     
        public void Cleanup()
        {           
            if (this.payloadId == null)
            {
                return;
            }
            int refCount = this.vmRoleResourceExtensionViewModel.ApplicationProvileViewModel.GetApplicationPayloadReferenceCount(this.PayloadId);
            if (refCount <= 1)
            {
                // Delete this
                // Check if selected path really exists
                if (this.CheckIfRelativePathRefersToRealPackage())
                {
                    // delete it in working dir datapckage file
                    string dataPkgLocationPath = this.GetDataPackageFolderPath();                 
                    string resolvedfullPath = Path.Combine(dataPkgLocationPath, this.selectedAppPayloadItemPath);
                    if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
                    {
                        Directory.Delete(resolvedfullPath, true); //recursive delete
                    }
                    else
                    {
                        File.Delete(resolvedfullPath);
                    }
                }  
                this.CleanupExternalPayload();
                if (this.selectedApplicationPayload != null)
                {
                    this.vmRoleResourceExtensionViewModel.ApplicationProvileViewModel.RemoveApplicationPayload(this.selectedApplicationPayload);
                }                
            }                                
        }
      

        private void CleanupExternalPayload()
        {
             if (this.IsSelectedAppPayloadItemPathARelativePath())
             {
                 this.CleanupExternalPayloadStartingWithPrefix(this.selectedAppPayloadItemPath);
             }
             else
             {
                string leafName;
                string error = ValidateAndGetLeafNameFromAbsPath(this.selectedAppPayloadItemPath, ValidationHelper.IsPayloadTypeFolder(this.payloadType), out leafName);
                if (error != null)
                {
                    this.CleanupExternalPayloadStartingWithPrefix(leafName);
                }
             }
        }


        private void CleanupExternalPayloadStartingWithPrefix(string prefix)
        {
            this.vmRoleResourceExtensionViewModel.ResourceExtensionDataPackageViewModel.DeleteExternalPayloadStartingWithPrefix(prefix);         
        }


        private void ProcessUpdateForSelectedRelativePath()
        {
            this.selectedApplicationPayload = this.vmRoleResourceExtensionViewModel.ApplicationProvileViewModel.GetApplicationPayloadViewModelFromRelativePath(this.selectedAppPayloadItemPath);
            //Check if  selected path really exists
            if (this.CheckIfRelativePathRefersToRealPackage())
            {
                //if External Error
                if (this.IsExternal)
                {
                    // Add External Payload
                    this.AddExternalPayloadForRelativePath();
                    // Delete it
                    string dataPkgLocationPath = this.GetDataPackageFolderPath();
                    string resolvedfullPath = Path.Combine(dataPkgLocationPath, this.selectedAppPayloadItemPath);
                    if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
                    {
                        Directory.Delete(resolvedfullPath, true); //recursive delete
                    }
                    else
                    {
                        File.Delete(resolvedfullPath);
                    }
                }
                // Add Payload to this reference               
                if (this.selectedApplicationPayload != null)
                {
                    this.UpdateReferenceToExistingMatchedApplicationPayload();
                }
                else
                {
                    this.AddNewAppPayloadAndReferenceIt();
                }                
            }
            else
            {
                if (!this.IsExternal)
                {
                    string fmtError = string.Format(
                        "No package with relative path {0} exists in data package dir {1}",
                        this.selectedAppPayloadItemPath, this.GetDataPackageFolderPath());
                    MessageBox.Show(fmtError, DefinitionViewModel.ToolHeader);
                    return;
                }
                // Add Payload to this reference
                if (this.selectedApplicationPayload != null)
                {
                    this.UpdateReferenceToExistingMatchedApplicationPayload();
                }
                else
                {
                    this.AddNewAppPayloadAndReferenceIt();
                }                              
            }
        }

        private void ProcessUpdateForSelectedAbsolutePath()
        {
            Debug.Assert(!this.IsSelectedAppPayloadItemPathARelativePath());
          
            string leafName;
            string error = ValidateAndGetLeafNameFromAbsPath(this.selectedAppPayloadItemPath, ValidationHelper.IsPayloadTypeFolder(this.payloadType), out leafName);
            if (error != null)
            {
                MessageBox.Show(error, DefinitionViewModel.ToolHeader);
                return;
            }

            //is abs path referrign to folder under data package folder
            string resolvedTargetDataPackageFullPath;
            if (this.IsAbsolutePathReferencingUnderDataPackageLocation(leafName, out resolvedTargetDataPackageFullPath))
            {
                // get relative path and process update for selected relative path
                this.selectedAppPayloadItemPath = leafName;
                this.ProcessUpdateForSelectedRelativePath();
                return;
            }

            //this is someother package in a different directory. this must be copied to data package dir location
            if (!this.PackageExistsInSelectedExpectedLocation(this.selectedAppPayloadItemPath))
            {
                MessageBox.Show("Source Package does not exist @:" + this.selectedAppPayloadItemPath, DefinitionViewModel.ToolHeader);
                return;
            }

            if (this.PackageExistsInSelectedExpectedLocation(resolvedTargetDataPackageFullPath))
            {
                // Can overwrite target package?
                if (MessageBox.Show("Can overwrite target package", DefinitionViewModel.ToolHeader, MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
                {
                    //do no stuff
                    return;
                }               
            }
            //copy if not external
            if (this.IsExternal)
            {
                this.AddExternalPayloadForAbsolutePath();    
            }
            else
            {
                this.CopyPackageToTarget(this.selectedAppPayloadItemPath, resolvedTargetDataPackageFullPath);
            }
            // get relative path and process update for selected relative path
            this.selectedAppPayloadItemPath = leafName;
            this.ProcessUpdateForSelectedRelativePath();
        }


        private void AddExternalPayloadForAbsolutePath()
        {
            string leafName;
            string error = ValidateAndGetLeafNameFromAbsPath(this.selectedAppPayloadItemPath, ValidationHelper.IsPayloadTypeFolder(this.payloadType), out leafName);
            if (error != null)
            {
                MessageBox.Show(error, DefinitionViewModel.ToolHeader);
                return;
            } 
            if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
            {
                this.AddPayloadInDir(this.selectedAppPayloadItemPath, leafName);             
            }
            else
            {
                           
                var extPayload = this.vmRoleResourceExtensionViewModel.ResourceExtensionDataPackageViewModel.AddExternalPayload();
                extPayload.RelativePath = leafName;
                extPayload.RelativePath = extPayload.RelativePath.Replace("\\", "/");
                extPayload.Hash = ExternalPayloadHashHelper.CalculateFileSha2Hash(this.selectedAppPayloadItemPath);
            }       
        }

        private void AddExternalPayloadForRelativePath()
        {
            string dataPkgLocationPath = this.GetDataPackageFolderPath();
            string resolvedfullPath = Path.Combine(dataPkgLocationPath, this.selectedAppPayloadItemPath);
        
            if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
            {
                this.AddPayloadInDir(resolvedfullPath, this.selectedAppPayloadItemPath);
            }
            else
            {
                var extPayload = this.vmRoleResourceExtensionViewModel.ResourceExtensionDataPackageViewModel.AddExternalPayload();
                extPayload.RelativePath = this.selectedAppPayloadItemPath;
                extPayload.RelativePath = extPayload.RelativePath.Replace("\\", "/");
                extPayload.Hash = ExternalPayloadHashHelper.CalculateFileSha2Hash(resolvedfullPath);
            }
        }


        private void AddPayloadInDir(string dir, string rootDirName)
        {
            try
            {
                foreach (string file in Directory.GetFiles(dir))
                {                 
                    var extPayload = this.vmRoleResourceExtensionViewModel.ResourceExtensionDataPackageViewModel.AddExternalPayload();
                    int suffixStart = file.IndexOf(rootDirName, System.StringComparison.Ordinal);
                    Debug.Assert(suffixStart >= 0);
                    if (suffixStart >= 0)
                    {
                        extPayload.RelativePath = file.Substring(suffixStart);
                        extPayload.RelativePath = extPayload.RelativePath.Replace("\\", "/");
                        extPayload.Hash = ExternalPayloadHashHelper.CalculateFileSha2Hash(file);
                    }                    
                }
                foreach (string d in Directory.GetDirectories(dir))
                {
                    // Recursive
                    this.AddPayloadInDir(d, rootDirName);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error while adding a directory as external payload: " + ex.Message, DefinitionViewModel.ToolHeader);
            }
        }

        private void UpdateReferenceToExistingMatchedApplicationPayload()
        {
            Debug.Assert(this.selectedApplicationPayload != null);
            if (this.payloadId == null && this.selectedApplicationPayload.Id != null)
            {
                this.payloadId = this.selectedApplicationPayload.Id;
                this.selectedApplicationPayload.RelativePath = this.selectedAppPayloadItemPath; //this is now relative path
                this.appPayloadReferenceSetter(this.selectedApplicationPayload.Id);
            }
            if (this.payloadId != null)
            {
                string error = ValidationHelper.ValidateGuid(this.payloadId);
                if (error != null)
                {
                    MessageBox.Show("Payload Id :" + this.payloadId + " " + error, DefinitionViewModel.ToolHeader);
                    return;
                }
                this.selectedApplicationPayload.Id = this.payloadId;
                this.selectedApplicationPayload.RelativePath = this.selectedAppPayloadItemPath; //this is now relative path
                this.appPayloadReferenceSetter(this.selectedApplicationPayload.Id);
            }            
        }

        private void AddNewAppPayloadAndReferenceIt()
        {
            this.payloadId = Guid.NewGuid().ToString();
            ApplicationPayload payload = new ApplicationPayload();
            payload.RelativePath = this.selectedAppPayloadItemPath; //this is now relative path
            payload.ID = this.payloadId;
            this.vmRoleResourceExtensionViewModel.ApplicationProvileViewModel.AddApplicationPayload(payload);          
            this.appPayloadReferenceSetter(this.payloadId);          
        }

        
        public string this[string propName]
        {
            get 
            { 
                string error = null;
                switch (propName)
                {
                    case PropertyNames.SelectedAppPayloadItemPath:
                        error = this.ValidateSelectedAppPayloadItemPath();
                        break;
                    case PropertyNames.PayloadId:
                        error = this.ValidatePayloadId();
                         break;
                    case PropertyNames.IsExternal:
                        if (this.isExternalChanged)
                        {
                            error = "Press Update after flipping IsExternal to update external payload details";
                        }
                        break;
                }
                return error;
            }
        }


        private string ValidatePayloadId()
        {
            //1. valid guid
            string error = ValidationHelper.ValidateGuid(this.payloadId);
            if (error != null)
            {
                if ((this.payloadId != null) || this.isPayloadRequired)
                {
                    return error;
                }               
            }

            //2. Update button not clicked
            if (this.payloadId != this.appPayloadReferenceGetter())
            {
                return "Press Update button to process selected payload";
            }

            //3. not null or empty if mandatory
            if (string.IsNullOrEmpty(this.payloadId))
            {
                return this.isPayloadRequired ? "Payload is required but not selected" : null;
            }                        

            //4. Has update been processed if abs path specified
            if (!this.IsSelectedAppPayloadItemPathARelativePath())
            {
                return "Press Update button to process selected payload from abs path";
            }
           
            //5. If guid, check if app profile refers it
            this.selectedApplicationPayload = this.vmRoleResourceExtensionViewModel.ApplicationProvileViewModel.GetApplicationPayloadViewModel(this.payloadId);
            if (this.selectedApplicationPayload == null)
            {
                error = "Application Payload not found in application profile for the referenced payload id: " + this.payloadId;
                return error;
            }

            //5.  check if no duplicate exists
            IEnumerable<ApplicationPayloadViewModel> results =
                this.vmRoleResourceExtensionViewModel.ApplicationProvileViewModel.ApplicationPayloads.Where(applicationPayloadViewModel => this.payloadId.Equals(applicationPayloadViewModel.Id, StringComparison.OrdinalIgnoreCase));
            if (results.Count() > 1)
            {
                error = string.Format("Referenced Payload Id {0} is not unique. Other payload items use the same guid", this.payloadId);
                return error;
            }

            //6.  If referenced check the relative path & path
            if (string.CompareOrdinal(this.selectedApplicationPayload.RelativePath, this.selectedAppPayloadItemPath) != 0)
            {
                error = string.Format("Payload Mismatch in referferenced relative path {0} and selected path: {1}. Press update", this.selectedApplicationPayload.RelativePath, this.selectedAppPayloadItemPath);
                return error;
            }
            

            return null;
        }

        private string GetResolvedFullPath()
        {
            string dataPkgLocationPath = this.GetDataPackageFolderPath();
            string resolvedfullPath = Path.IsPathRooted(this.selectedAppPayloadItemPath)
                ? this.selectedAppPayloadItemPath : Path.Combine(dataPkgLocationPath, this.selectedAppPayloadItemPath);
            return resolvedfullPath;
        }


        private bool PayloadExists()
        {
            string resolvedfullPath = this.GetResolvedFullPath();
            if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
            {
                return ((Directory.Exists(this.selectedAppPayloadItemPath)) || (Directory.Exists(resolvedfullPath)));               
            }
            return (System.IO.File.Exists(this.selectedAppPayloadItemPath) || System.IO.File.Exists(resolvedfullPath));
        }

        private string ValidateSelectedAppPayloadItemPath()
        {
            string error;

            //1. If payload is not set
            if (string.IsNullOrEmpty(this.selectedAppPayloadItemPath))
            {
                // Is it required?
                if (this.isPayloadRequired)
                {
                    error = this.payloadType + " Payload must be specified";
                    return error;
                }
                // Mistmatch in app payload id reference
                if (!string.IsNullOrEmpty(this.appPayloadReferenceGetter()))
                {
                    error = " Payload is not specified, but payload id is set. Clear by pressing update";
                    return error;
                }

                return null;
            }

            //2. Basic checks
            error = this.ValidateBasicRequirementsForSelectedAppPayloadItemPath();
            if (error != null)
            {
                return error;
            }

            //3. Check if seletec folder/file does not exist
            string dataPkgLocationPath = this.GetDataPackageFolderPath();       
            bool externalAndDoesNotExist = false;
            if (!this.PayloadExists())
            {
                if (!this.IsExternal)
                {
                    if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
                    {
                        error = "Selected package dir does not exist: " + this.selectedAppPayloadItemPath;
                    }
                    else
                    {
                        error = "Selected package does not exist: " + this.selectedAppPayloadItemPath;
                    }                    
                    return error;
                }
                externalAndDoesNotExist = true;
            }           

            // 4.  If not using relative path, check if package/package dir has been copied to datapackage working dir location
            bool usingRootedPath = Path.IsPathRooted(this.selectedAppPayloadItemPath);
            if (usingRootedPath && !externalAndDoesNotExist)
            {
                string relativeName;
                error = ValidateAndGetLeafNameFromAbsPath(this.selectedAppPayloadItemPath, ValidationHelper.IsPayloadTypeFolder(this.payloadType), out relativeName);
                if (error != null)
                {
                    return error;
                }
                string expectedLocation = Path.Combine(dataPkgLocationPath, relativeName);
                if (!this.PackageExistsInSelectedExpectedLocation(expectedLocation))
                {
                    error = "Press update to copy package to data package location: " + expectedLocation;
                    return error;
                }
            }

            // 5. File extensions check
            if (!ValidationHelper.IsPayloadTypeFolder(this.payloadType))
            {
                string ext = Path.GetExtension(this.selectedAppPayloadItemPath);
                switch (this.payloadType)
                {
                     case ValidationHelper.PayloadType.WebDeployPackage:
                        if (string.Compare(ext, ".zip", StringComparison.OrdinalIgnoreCase) != 0)
                        {
                            error = "Expected Web Deploy Package (*.zip)";
                            return error;
                        }
                        break;
                      case ValidationHelper.PayloadType.SqlDac:
                        if (string.Compare(ext, ".dacpac", StringComparison.OrdinalIgnoreCase) != 0)
                        {
                            error = "Expected SQL Dac Package (*.dacpac)";
                            return error;
                        }
                        break;
                    case ValidationHelper.PayloadType.SqlScript:
                        if (string.Compare(ext, ".sql", StringComparison.OrdinalIgnoreCase) != 0)
                        {
                            error = "Expected SQL Script (*.sql)";
                            return error;
                        }
                        break;
                    case ValidationHelper.PayloadType.SQLConfigurationFile:
                        if (string.Compare(ext, ".ini", StringComparison.OrdinalIgnoreCase) != 0)
                        {
                            error = "Expected SQL Configuration File (*.ini)";
                            return error;
                        }
                        break;
                }
            }              

            //6. Script payload folder must end in ".cr" // no longer necessary
            //switch (this.payloadType)
            //{
            //    case ValidationHelper.PayloadType.ScriptFolder:
            //        if (!this.selectedAppPayloadItemPath.EndsWith(".cr", StringComparison.OrdinalIgnoreCase))
            //        {
            //            error = "Script payload folder name must end in '.cr'";
            //            return error;
            //        }
            //        break;
            //}          

            return null;
        }

        private bool CheckIfRelativePathRefersToRealPackage()
        {
            string dataPkgLocationPath = this.GetDataPackageFolderPath();         
            string resolvedfullPath = Path.Combine(dataPkgLocationPath, this.selectedAppPayloadItemPath);
            if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
            {
                return Directory.Exists(resolvedfullPath);
            }
            return File.Exists(resolvedfullPath);
        }

        private void CopyPackageToTarget(string sourceFullPath, string destinationFullPath)
        {            
            if (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
            {
                DirectoryCopy(sourceFullPath, destinationFullPath);
            }
            else
            {
                // overwrite the destination file if it already exists.
                System.IO.File.Copy(sourceFullPath, destinationFullPath, true);
            }
        }

        public static void DirectoryCopy(string sourceDirName, string destDirName)
        {
            DirectoryInfo dir = new DirectoryInfo(sourceDirName);
            DirectoryInfo[] dirs = dir.GetDirectories();

            if (!dir.Exists)
            {
                throw new DirectoryNotFoundException("Source directory does not exist or could not be found: " + sourceDirName);
            }

            if (!Directory.Exists(destDirName))
            {
                Directory.CreateDirectory(destDirName);
            }

            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                string temppath = Path.Combine(destDirName, file.Name);
                file.CopyTo(temppath, true);
            }
            foreach (DirectoryInfo subdir in dirs)
            {
                string temppath = Path.Combine(destDirName, subdir.Name);
                DirectoryCopy(subdir.FullName, temppath);
            }      
        }

        private string GetDataPackageFolderPath()
        {
            string dataPkgLocationPath = Path.Combine(
                this.vmRoleResourceExtensionViewModel.ResourceExtensionStorageInfo.WorkingDir, 
                this.vmRoleResourceExtensionViewModel.ResourceExtensionDefinition.DataPackage.Location);
            return dataPkgLocationPath;
        }

        private bool IsSelectedAppPayloadItemPathARelativePath()
        {
            return !Path.IsPathRooted(this.selectedAppPayloadItemPath);
        }

     
        private bool IsAbsolutePathReferencingUnderDataPackageLocation(string leafName, out string resolvedTargetDataPackageFullPath)
        {
            string dataPkgLocationPath = this.GetDataPackageFolderPath();
            resolvedTargetDataPackageFullPath = Path.Combine(dataPkgLocationPath, leafName);

            return (String.Compare(Path.GetFullPath(this.selectedAppPayloadItemPath).TrimEnd('\\'),
                               Path.GetFullPath(resolvedTargetDataPackageFullPath).TrimEnd('\\'),
                               StringComparison.InvariantCultureIgnoreCase) == 0) ;
        }

        private static string ValidateAndGetLeafNameFromAbsPath(string path, bool isDirectory, out string leafName)
        {
            string error;
            leafName = null;
            if (isDirectory)
            {
                var separators = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
                string fullFolderPath = Path.GetFullPath(path).TrimEnd('\\');
                var entries = fullFolderPath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
                if (entries.Length == 0)
                {
                    error = "Incorrect directory specified";
                    return error;
                }
                string foldername = entries[entries.Length - 1];
                if (string.IsNullOrEmpty(foldername))
                {
                    error = "Leaf Foldername invalid: " + fullFolderPath;
                    return error;
                }
                leafName = foldername;             
            }
            else
            {
                string filename = System.IO.Path.GetFileName(path);
                if (string.IsNullOrEmpty(filename))
                {
                    error = "Incorrect filename specified";
                    return error;
                }
                leafName = filename;                         
            }
            return null;
        }

        private string ValidateBasicRequirementsForSelectedAppPayloadItemPath()
        {
            string error;
            if (this.vmRoleResourceExtensionViewModel.ResourceExtensionStorageInfo == null)
            {
                error = "Disk storage info such as working package dir is not set";
                return error;
            }

            if (!Directory.Exists(this.vmRoleResourceExtensionViewModel.ResourceExtensionStorageInfo.WorkingDir))
            {
                error = "Working package dir does not exist: " + this.vmRoleResourceExtensionViewModel.ResourceExtensionStorageInfo.WorkingDir;
                return error;
            }

            if (this.vmRoleResourceExtensionViewModel.ResourceExtensionDefinition.DataPackage == null ||
                string.IsNullOrEmpty(this.vmRoleResourceExtensionViewModel.ResourceExtensionDefinition.DataPackage.Location))
            {
                error = "Datapackage Location not set";
                return error;
            }

            string dataPkgLocationPath = this.GetDataPackageFolderPath();
            if (!Directory.Exists(this.vmRoleResourceExtensionViewModel.ResourceExtensionStorageInfo.WorkingDir))
            {
                error = "Datapackage working dir does not exist: " + dataPkgLocationPath;
                return error;
            }
            return null;
        }

        private bool PackageExistsInSelectedExpectedLocation(string expectedLocation)
        {
            return (ValidationHelper.IsPayloadTypeFolder(this.payloadType))
                       ? Directory.Exists(expectedLocation)
                       : File.Exists(expectedLocation);
        }      

        public string Error { get; set; }
    }
}
